package com.yinhd.study.threadTest;

/**
 * @program: study
 * @description:
 * @Author: yinhd
 * @create: 2022-07-06 15:24
 **/

import com.yinhd.study.dockingPlatform.entity.Face;
import lombok.Data;

import java.text.SimpleDateFormat;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 引入：
 * 典型场景1：每个线程需要一个独享的对象（通常是工具类，典型需要使用的类有SimpleDateFormat和Random）
 * 典型场景2：每个线程内需要保存全局变量（例如在拦截器中获取用户信息），可以让不同方法直接使用，避免参数
 * 传递的麻烦。
 * 另外还用满足这样一种情况：不同线程有不同的业务对象，比如不同用户访问下单，装填购物车所需要的用户信息
 * 不同，保证对象信息在线程之间的隔离
 * -ConcurrentHashMap?
 * --底层是synchronized 影响性能
 * -ThreadLocal?
 * --使用ThreadLocal 业务信息在同一线程内相同，但是在不同线程使用的业务内容是不同的。
 * --通过使用静态的ThreaLocal实例的get()方法可随时取出本线程需要的业务对象，同时避免了参数
 * 相互传递的麻烦
 */

//用法二： 不同线程之间业务对象的隔离 + 避免同一参数在不同方法之间来回传递

// ThreadLocal 好处
// 线程安全
// 不需要加锁
// 更高效的利用内存资源，节省开销：相比于每个任务都新建一个SimpleDateFormat,显然用Thread Local可以节省内存和开销
// 免去了传参的繁琐：无论是场景一的工具类还是场景二自定义的用户名，都有可以直接通过ThreadLocal拿到，再也不需要每次都传同样的参数
// ThreadLocal使得代码耦合度更低,更优雅

// 在开发中你可能关于遇到这样的场景：
// 1.在多线程的开发场景中，对于一些共享资源，且该共享资源不能保证线程安全，那么我们通常会给该资源进行加
//   锁的操作，如果加了锁，那么在并发情况下，并发线程在这就要进行排队了
// 2.同一个资源在涉及此次请求的每个方法都用到了，那么你可能会在每个方法出进行参收接受
// 对于上述情况，是否有个工具去解决？ ---ThreadLocal



public class ThreadLocalTest2 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            Service1 service1 = new Service1();
            Face face = new Face();
            face.setFaceID(String.valueOf(i));
            service1.setFace(face);
            executorService.submit(service1);
        }
    }
}

@Data
class Service1 implements Runnable {
    Face face;
    public void process(Face face) {
        Hoder.local.set(face);
        //SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get();
        Service2 service2 = new Service2();
        service2.process();
    }

    @Override
    public void run() {
        process(this.face);
    }
}

class Service2 {
    public void process() {
        Face face = Hoder.local.get();
        System.out.println("Service2 " +Thread.currentThread().getName()+" "+ face.getFaceID());
        Service3 service3 = new Service3();
        service3.process();
    }
}

class Service3 {
    public void process() {
        Face face = Hoder.local.get();
        System.out.println("Service3 " +Thread.currentThread().getName()+" " + face.getFaceID());
    }
}

class Hoder {
    public static ThreadLocal<Face> local = new ThreadLocal<>();
}


